#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <unistd.h>
#include <sys/epoll.h>
using namespace std;

class epollServer
{
    static const int maxsize = 64;

private:
    // Tcp工作
    void GetTcpSock()
    {
        // 创建一个TCP listen sock
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            cout << "创建TCP listen sock 失败" << endl;
            exit(-1);
        }
        cout << "创建TCP listen sock 成功! listensockfd:" << _listensockfd << endl;

        // 设置地址复用
        int opt = 1;
        setsockopt(_listensockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 绑定
        struct sockaddr_in serveraddr;
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");
        serveraddr.sin_port = htons(_port);
        int n = bind(_listensockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        if (n < 0)
        {
            cout << "绑定失败" << endl;
            exit(-1);
        }
        cout << "绑定成功" << endl;

        // 设置listensockfd为监听状态
        static const int gbacklog = 8;
        n = listen(_listensockfd, gbacklog);
        if (n < 0)
        {
            cout << "listen sock fd设置监听状态失败" << endl;
            exit(-1);
        }
        cout << "listen sock fd设置监听状态成功" << endl;
    }

public:
    epollServer(int port)
        : _port(port),
          _listensockfd(-1),
          _epfd(-1),
          _timeout(1000)
    {
        // 获取TCP连接
        GetTcpSock();
        // 创建epoll模型
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            cout << "epoll模型创建失败" << endl;
            exit(-1);
        }
        cout << "epoll模型创建成功 epfd:" << _epfd << endl;

        // 将listensockfd添加至epoll模型红黑树中
        struct epoll_event ev;
        ev.data.fd = _listensockfd;
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensockfd, &ev);
        if (n < 0)
        {
            cout << "添加listensockfd添加至epoll模型红黑树中-失败" << endl;
            exit(-1);
        }
        cout << "添加listensockfd添加至epoll模型红黑树中-成功" << endl;
    }

    // 获取连接操作
    void ClientAccept()
    {
        struct sockaddr_in clientaddr;
        socklen_t len = sizeof(clientaddr);
        int newfd = accept(_listensockfd, (struct sockaddr *)&clientaddr, &len);
        if (newfd < 0)
        {
            // 获取连接失败
            return;
        }
        cout << "server获取了一个新连接, fd:" << newfd << " [ip:" << inet_ntoa(clientaddr.sin_addr) << " port:" << ntohs(clientaddr.sin_port) << "]" << endl;
        // 将这个新事件添加到红黑树中
        struct epoll_event ev;
        ev.data.fd = newfd;
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &ev);
        if (n < 0)
        {
            cout << "将fd:" << newfd << "添加至红黑树中-失败" << endl;
            close(newfd);
            cout << "已关闭此新获取的连接, fd:" << newfd << endl;
        }
        else
        {
            cout << "将fd:" << newfd << "添加至红黑树中-成功" << endl;
        }
    }
    // IO通信操作
    void ServiceIO(int sockfd)
    {
        char inbuffer[1024]; // 接收缓冲区
        ssize_t n = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);
        if (n > 0)
        {
            // 读取成功
            inbuffer[n] = '\0';
            cout << "server收到来自client的信息:" << inbuffer << endl;
            string outbuffer = "[Server Echo] ### ";
            outbuffer += inbuffer;
            int m = send(sockfd, outbuffer.c_str(), outbuffer.size(), 0);
            if (m < 0)
            {
                cout << "server发送信息失败#" << inbuffer << endl;
            }
            cout << "server发送信息成功#" << inbuffer << endl;
        }
        else if (n == 0)
        {
            // 客户端退出
            // 将客户端信息从红黑树中删除
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            cout << "客户端退出,已将fd#" << sockfd << " 从红黑树中删除" << endl;
            // 关闭此文件描述符
            close(sockfd);
            cout << "已将fd#" << sockfd << " 文件描述符-关闭" << endl;
        }
        else
        {
            // 读取失败
            // 将客户端信息从红黑树中删除
            epoll_ctl(_epfd, EPOLL_CTL_DEL, sockfd, nullptr);
            cout << "读取失败,已将fd#" << sockfd << " 从红黑树中删除" << endl;
            // 关闭此文件描述符
            close(sockfd);
            cout << "已将fd#" << sockfd << " 文件描述符-关闭" << endl;
        }
    }

    // 处理就绪事件
    void HanderEvents(int num)
    {
        // 遍历缓冲区中前num个事件，即为本轮要处理的就绪事件
        for (int i = 0; i < num; i++)
        {
            // 判断读事件就绪
            if (_data[i].events & EPOLLIN > 0)
            {
                // 判断listensockfd、sockfd
                if (_data[i].data.fd == _listensockfd)
                {
                    // 获取连接操作
                    ClientAccept();
                }
                else
                {
                    // IO通信操作
                    ServiceIO(_data[i].data.fd);
                }
            }
            // 判断写事件就绪
            if (_data[i].events & EPOLLOUT > 0)
            {
            }
        }
    }

    // 启动
    void Loop()
    {
        while (true)
        {
            int n = epoll_wait(_epfd, _data, maxsize, _timeout);
            if (n < 0)
            {
                // epoll_wait函数出错
                cout << "epoll_wait函数出错了" << endl;
            }
            else if (n == 0)
            {
                // epoll_wait超时了
                cout << "epoll_wait函数超时了" << endl;
            }
            else
            {
                // epoll_wait有事件就绪
                cout << "epoll_wait函数检测到" << n << "个事件已就绪" << endl;
                // 处理就绪事件
                HanderEvents(n);
            }
        }
    }
    ~epollServer()
    {
        // 将缓冲区中的数据清空
        for (int i = 0; i < maxsize; i++)
        {
            _data[i].data.fd = -1;
            _data[i].events = 0;
        }
        cout << "缓冲区中的数据已被清空" << endl;
        if (_listensockfd > 0)
        {
            close(_listensockfd);
            cout << "listen sock fd:" << _listensockfd << "已被操作系统关闭" << endl;
        }
        if (_epfd > 0)
        {
            close(_epfd);
            cout << "listen sock fd:" << _epfd << "已被操作系统关闭" << endl;
        }
    }

private:
    int16_t _port;                     // 端口号
    int _listensockfd;                 // listen sockfd
    int _epfd;                         // epoll sockfd
    struct epoll_event _data[maxsize]; // 缓冲区
    int _timeout;                      // 等待策略
};
